{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 107 TCP和UDP协议解读"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 关注维度 | 核心要点                                                     | 推荐实践                                                     | 易忽视风险                                            |\n",
    "| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------------------------------------- |\n",
    "| 协议定位 | TCP/UDP 均位于 **传输层**，负责主机之间的端到端通信          | 根据业务需求选择：有序可靠选 TCP，低延迟/容错高选 UDP        | 误以为“TCP 更快”；实际上可靠性带来额外开销            |\n",
    "| 连接状态 | TCP 是**有连接**协议（需三次握手、四次挥手），UDP 是**无连接**协议 | TCP 用 `net.Listen` + `net.Dial`；UDP 用 `net.ListenUDP` + `net.DialUDP` | 忽略 TCP 握手状态可能引发 SYN Flood 风险              |\n",
    "| 可靠性   | TCP 提供序号、确认号、重传、拥塞控制；UDP 不保证可靠性/顺序  | 在不稳定网络使用 TCP；内部 LAN、实时音视频可采用 UDP + 应用层重传 | UDP 丢包/乱序需上层协议兜底；TCP 过度重传可能拖慢性能 |\n",
    "| 首部开销 | TCP 首部至少 20 字节，可携带选项（最多 60 字节）；UDP 首部固定 8 字节 | MTU 较小时优先 UDP 或调整 MSS/TCP options                    | 未考虑 MSS/MTU 可能导致分片，降低效率                 |\n",
    "| 应⽤场景 | TCP：HTTP、数据库、文件传输、消息队列等可靠场景；UDP：DNS、DHCP、线上游戏、语音视频 | TCP 常配合 keepalive/心跳、TLS；UDP 应用层需定义重传与校验   | UDP 广播 storm、TCP TIME_WAIT 过多等需注意            |\n",
    "| 编程模型 | TCP 面向流（Stream），需要协议划分边界；UDP 面向报文（Datagram），一次收发对应一条消息 | TCP 应在应用层自定义帧格式（长度字段/分隔符）；UDP 执行 `ReadFromUDP`/`WriteToUDP` | 忽略 TCP 粘包/拆包                                    |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要点\n",
    "\n",
    "1. **分层设计思想**\n",
    "   - 数据从应用层一路向下添加头部：应用层报文（datagram）→ 传输层分段（segment）→ 网络层分组（packet）→ 数据链路层帧（frame）。\n",
    "   - 每层头部如同“信封套娃”，最后通过物理层介质传输。MTU 限制会决定单帧最大长度（以太网常见 1500 字节），传输层需调节 MSS 防止分片。\n",
    "2. **OSI vs TCP/IP 模型**\n",
    "   - OSI 七层：物理 → 数据链路 → 网络 → 传输 → 会话 → 表示 → 应用。\n",
    "   - TCP/IP 四层：网络接口（物理+链路）→ 网络层（IP）→ 传输层（TCP/UDP）→ 应用层。\n",
    "   - Go 应用开发主要与应用层/传输层打交道，其余交由内核处理。\n",
    "3. **TCP 报文结构**\n",
    "   - 首部字段：源端口、目的端口、序号（Sequence Number）、确认号（ACK Number）、数据偏移（首部长度）、标志位（SYN/ACK/FIN/RST/PSH/URG）、窗口大小、校验和、紧急指针。\n",
    "   - 数据偏移最小值 5（表示 20 字节固定首部），最大 15（60 字节，包含选项）。\n",
    "   - 连接建立三次握手（`SYN` → `SYN+ACK` → `ACK`），拆除四次挥手(`FIN, ACK`、`ACK`、`FIN, ACK`、`ACK` )。SYN Flood 利用握手机制攻击服务器半连接队列。\n",
    "4. **UDP 报文结构**\n",
    "   - 固定 8 字节：源端口、目的端口、长度、校验和。\n",
    "   - 无连接、不保证顺序、不可靠，但头部小、延迟低、适合内网和实时应用（DNS、VoIP、游戏）。\n",
    "\n",
    "1. **Socket 概念**\n",
    "   - Socket 是进程之间通信的端点，包含 IP + 端口 + 协议。一个端口可以被多个进程在不同协议（TCP/UDP）上使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TCP 客户端/服务端（含三次握手流程）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 建立链接"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go  \n",
    "\n",
    "[server] TCP listening on 127.0.0.1:9090\n",
    "[server] new connection from 127.0.0.1:48544"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go\n",
    "\n",
    "[client] connected to server. Try commands: PING / TIME / any text / QUIT\n",
    "> "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!  sudo tcpdump -vvv -i lo -nn \\\n",
    "  'tcp port 9090 and ((tcp[13] & 0x02 != 0) or (tcp[13] & 0x01 != 0) or (tcp[13] & 0x04 != 0) or (tcp[13] = 0x10))'\n",
    "tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n",
    "22:14:06.655451 IP (tos 0x0, ttl 64, id 37550, offset 0, flags [DF], proto TCP (6), length 60)\n",
    "    127.0.0.1.37458 > 127.0.0.1.9090: Flags [S], cksum 0xfe30 (incorrect -> 0x561e), seq 2894079445, win 65495, options [mss 65495,sackOK,TS val 757875853 ecr 0,nop,wscale 7], length 0\n",
    "22:14:06.655525 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)\n",
    "    127.0.0.1.9090 > 127.0.0.1.37458: Flags [S.], cksum 0xfe30 (incorrect -> 0x44bb), seq 2248022438, ack 2894079446, win 65483, options [mss 65495,sackOK,TS val 757875853 ecr 757875853,nop,wscale 7], length 0\n",
    "22:14:06.655535 IP (tos 0x0, ttl 64, id 37551, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.37458 > 127.0.0.1.9090: Flags [.], cksum 0xfe28 (incorrect -> 0x6b77), seq 1, ack 1, win 512, options [nop,nop,TS val 757875853 ecr 757875853], length 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 客户端退出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go \n",
    "[server] TCP listening on 127.0.0.1:9090\n",
    "[server] new connection from 127.0.0.1:42668\n",
    "[server] received \"QUIT\" from 127.0.0.1:42668\n",
    "[server] closing connection with 127.0.0.1:42668"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go\n",
    "[client] connected to server. Try commands: PING / TIME / any text / QUIT\n",
    "> QUIT"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! sudo tcpdump -vvv -i lo -nn   'tcp port 9090 and ((tcp[13] & 0x02 != 0) or (tcp[13] & 0x01 != 0) or (tcp[13] & 0x04 != 0) or (tcp[13] = 0x10))'\n",
    "tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n",
    "22:16:34.497595 IP (tos 0x0, ttl 64, id 7907, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.9090 > 127.0.0.1.42668: Flags [.], cksum 0xfe28 (incorrect -> 0xe6db), seq 2416812762, ack 4002758077, win 512, options [nop,nop,TS val 758023692 ecr 758023692], length 0\n",
    "22:16:34.497702 IP (tos 0x0, ttl 64, id 49161, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.42668 > 127.0.0.1.9090: Flags [.], cksum 0xfe28 (incorrect -> 0xe6d7), seq 1, ack 4, win 512, options [nop,nop,TS val 758023692 ecr 758023692], length 0\n",
    "22:16:34.497777 IP (tos 0x0, ttl 64, id 7909, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.9090 > 127.0.0.1.42668: Flags [F.], cksum 0xfe28 (incorrect -> 0xe6d5), seq 4, ack 1, win 512, options [nop,nop,TS val 758023693 ecr 758023692], length 0\n",
    "22:16:34.497780 IP (tos 0x0, ttl 64, id 49162, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.42668 > 127.0.0.1.9090: Flags [F.], cksum 0xfe28 (incorrect -> 0xe6d5), seq 1, ack 4, win 512, options [nop,nop,TS val 758023693 ecr 758023692], length 0\n",
    "22:16:34.497787 IP (tos 0x0, ttl 64, id 7910, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.9090 > 127.0.0.1.42668: Flags [.], cksum 0xfe28 (incorrect -> 0xe6d3), seq 5, ack 2, win 512, options [nop,nop,TS val 758023693 ecr 758023693], length 0\n",
    "22:16:34.497794 IP (tos 0x0, ttl 64, id 49163, offset 0, flags [DF], proto TCP (6), length 52)\n",
    "    127.0.0.1.42668 > 127.0.0.1.9090: Flags [.], cksum 0xfe28 (incorrect -> 0xe6d3), seq 2, ack 5, win 512, options [nop,nop,TS val 758023693 ecr 758023693], length 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### UDP 客户端/服务端（无连接、报文模式）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go  \n",
    "[udp-server] listening on 127.0.0.1:7070\n",
    "[udp-server] from 127.0.0.1:36564: \"TIME\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go\n",
    "[udp-client] connected logically (no handshake). Try: PING / TIME / text / Ctrl+C\n",
    "> TIME\n",
    "[udp-client] reply: 22:20:01\n",
    "> "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 108 TCP编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 自带的 `net` 标准库对 TCP/IP 协议提供了一套优雅、易用的抽象，重点体现在以下几个方面：\n",
    "\n",
    "1. **监听与建立连接（Listen / Dial）**\n",
    "   - 服务器通过 `net.Listen` / `net.ListenTCP` 在指定地址和端口侦听。\n",
    "   - 客户端使用 `net.Dial` / `net.DialTCP` 主动发起连接。\n",
    "   - 底层依旧遵循 TCP 三次握手与四次挥手，但上层 API 已经屏蔽了细节。\n",
    "2. **连接生命周期管理**\n",
    "   - `net.Conn` 接口封装了 `Read`、`Write`、`Close` 等方法。\n",
    "   - 可以通过 `SetDeadline` / `SetReadDeadline` / `SetWriteDeadline` 防止读写永久阻塞。\n",
    "   - 主动关闭方调用 `Close()` 后触发 FIN 报文；被动方若继续读取，也会在读到 `io.EOF` 时获知对端关闭。\n",
    "3. **并发与协程（Goroutines）**\n",
    "   - Go 的轻量级协程和通道（channels）让我们能够轻松实现高并发 TCP 服务。\n",
    "   - 常见模式：监听端口后对每个 `Accept()` 出来的连接启动一个 goroutine 进行处理。\n",
    "4. **TCP 粘包与拆包问题**\n",
    "   - TCP 是字节流协议，没有消息边界，需要应用层自行约定协议格式。\n",
    "   - 常见策略包括固定长度、分隔符、长度字段等。\n",
    "   - Go 中可用 `bufio.Scanner`、`bufio.Reader`、自定义分隔逻辑等方式处理。\n",
    "5. **调试与排障**\n",
    "   - 使用 `tcpdump`、Wireshark 等抓包工具观察 SYN/ACK/FIN 报文。\n",
    "   - 关注 `TIME_WAIT`、`CLOSE_WAIT` 等状态，合理设置超时与连接复用策略。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TCP 连接生命周期\n",
    "\n",
    "1. **三次握手**：客户端 `SYN` → 服务器 `SYN+ACK` → 客户端 `ACK`，连接建立。\n",
    "2. **数据收发**：双方在全双工通道上读写字节流，应用层需自行定义消息边界。\n",
    "3. **四次挥手**：关闭连接时双方互发 `FIN/ACK`，若抓包可以看到典型的四个报文。\n",
    "4. **超时控制**：`SetDeadline` / `SetReadDeadline` / `SetWriteDeadline` 用于防止读写阻塞。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 109 UDP编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**协议特性**：UDP（User Datagram Protocol）是无连接、无状态的传输层协议，依赖尽力而为的交付机制，不保证可靠性、顺序性与重复控制，但拥有更低的时延和更小的协议开销。\n",
    "\n",
    "**Socket 抽象**：Go 的 `net` 包对 UDP 与 TCP 提供统一的 API 外壳。相较 TCP，UDP 不需要握手，也没有持续的连接状态，应用层需自行处理丢包、重传等问题。\n",
    "\n",
    "**典型场景**：实时音视频、游戏同步、日志/监控上报等对时延敏感、可容忍少量丢失的业务。\n",
    "\n",
    "**编程思路**：\n",
    "\n",
    "- 服务器端：`ResolveUDPAddr → ListenUDP → ReadFromUDP/WriteToUDP`。\n",
    "- 客户端：`DialTimeout → Write/Read`。\n",
    "- 单个服务端 socket 可以同时对接多个客户端，回复时必须带上目标地址。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 服务端"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**创建地址**\n",
    "\n",
    "- `net.ResolveUDPAddr(\"udp\", \"127.0.0.1:5678\")` 返回 `*net.UDPAddr`。\n",
    "- 协议必须指定为 `\"udp\"`，端口固定在服务器侧，客户端端口由操作系统动态分配。\n",
    "\n",
    "**监听 UDP**\n",
    "\n",
    "- `net.ListenUDP()` 立即返回 `*net.UDPConn`，不会像 TCP 那样阻塞等待连接。\n",
    "- 一个 socket 可服务多个客户端；确保使用 `defer conn.Close()` 释放资源。\n",
    "- 可在 `ListenUDP` 后立即打印日志验证非阻塞特性。\n",
    "\n",
    "**读取数据**\n",
    "\n",
    "- 使用 `conn.ReadFromUDP()` 获取报文与发送者地址。\n",
    "- 需要提前准备缓冲区（如 `make([]byte, 256)`）。\n",
    "- `n, remoteAddr, err := conn.ReadFromUDP(buf)`。\n",
    "- 多客户端场景下，服务端需要记录 `remoteAddr`，并通过 `WriteToUDP()` 定向回复。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 客户端"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**创建连接**\n",
    "\n",
    "- `net.DialTimeout(\"udp\", serverAddr, 3*time.Minute)` 直接建立面向服务器的“伪连接”。\n",
    "- Dial 会自动处理 DNS 解析、路由选择、端口分配（客户端端口随机）。\n",
    "- 可通过 `conn.LocalAddr()` 查看本地端口。\n",
    "\n",
    "**写数据**\n",
    "\n",
    "- `conn.Write([]byte(\"hello\"))` 即可发送数据，无需指定目标地址。\n",
    "- 返回写入字节数与错误信息；即使服务器未启动，写操作通常也不会立刻失败。\n",
    "\n",
    "**测试与验证**\n",
    "\n",
    "- 服务端日志会显示收到的消息及客户端端口（如 `62905`）。\n",
    "- 客户端可打印发送的字节数和本地端口。\n",
    "- 关键差异：服务端回复必须使用 `WriteToUDP()` 指定客户端地址，这是 UDP 无连接特性的体现。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 110 长连接"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 关注维度 | 核心要点                                             | 推荐实践                                                     | 易忽视风险                                    |\n",
    "| -------- | ---------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------- |\n",
    "| 协议定位 | 长连接依托 TCP 传输层，允许同一通道承载多轮请求/响应 | 需在应用层定义会话协议、消息格式、心跳规则                   | 忽略协议分帧导致消息边界不明、调试困难        |\n",
    "| 连接状态 | 建立一次三次握手后持续复用，服务器需维护连接上下文   | `net.Listen` + `Accept` 建立，客户端复用 `net.Conn` 多次读写 | 未做闲置检测导致僵尸连接、资源泄露            |\n",
    "| 可用性   | 需处理断线重连、读写超时、心跳超时、粘包             | `SetDeadline`、应用层 Ping/Pong、重连策略                    | 只依赖 TCP KeepAlive，检测不及时              |\n",
    "| 资源治理 | 长时间占用 FD、内存与 goroutine，需要限流与监控      | 调高 `ulimit`/`somaxconn`、连接池、定期心跳清理              | 忽视异常（慢读、半关闭）导致连接风暴          |\n",
    "| 应用场景 | IM/推送、实时行情、在线游戏、IoT 会话管理、撮合交易  | 结合鉴权、订阅、消息路由、分布式会话管理                     | 直接堆业务逻辑在单连接上，缺乏分层设计        |\n",
    "| 编程模式 | TCP 是字节流，需自定义分帧（长度、分隔符、序列化）   | 使用 bufio、长度前缀、ProtoBuf/JSON-RPC 等协议               | 不处理粘包/半包，出现读写错乱                 |\n",
    "| 安全策略 | 需与鉴权、限速、心跳配合，防御恶意占用               | 模拟异常场景（心跳丢失、慢读攻击）提前验证                   | 心跳频率过高/过低均可能带来额外开销或检测盲区 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 服务器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**功能说明**：监听 `127.0.0.1:9000`，针对每个客户端连接保持长连接，循环读取按行分隔的业务消息；支持 `PING`/`PONG` 心跳；若超过 30 秒未收到任何数据则主动断开。\n",
    "\n",
    "**关键要点**\n",
    "\n",
    "1. `SetReadDeadline` 防止空闲连接霸占资源。\n",
    "2. `bufio.Scanner` 按行解析，示范如何约定消息边界。\n",
    "3. 心跳请求与业务消息分支处理。\n",
    "4. `defer conn.Close()` 确保连接正确释放。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go \n",
    "2025/10/01 00:42:26 长连接服务器已启动，监听 127.0.0.1:9101 ...\n",
    "2025/10/01 00:41:26 新连接 remote=127.0.0.1:48842 local=127.0.0.1:9101\n",
    "2025/10/01 00:41:26 业务消息来自 127.0.0.1:48842 => \"login user=alice token=123456\"\n",
    "2025/10/01 00:41:28 业务消息来自 127.0.0.1:48842 => \"publish room=42 body=HelloLongConn\"\n",
    "2025/10/01 00:41:30 业务消息来自 127.0.0.1:48842 => \"query order_id=2025001 status\"\n",
    "2025/10/01 00:47:51 对端主动关闭：127.0.0.1:48842\n",
    "2025/10/01 00:47:51 连接关闭 remote=127.0.0.1:48842"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 客户端"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**功能说明**：连接服务器后，发送三条业务消息并定期发送心跳；同时在后台 goroutine 中读取服务器回显/心跳回应。\n",
    "\n",
    "**关键要点**\n",
    "\n",
    "1. 单次 `net.Dial` 后复用连接发送多条消息。\n",
    "2. 使用 goroutine 和 `bufio.Scanner` 持续读取服务器返回数据。\n",
    "3. 使用 `Ticker` 周期性发送心跳，保持连接活跃。\n",
    "4. 遇到写入失败/读取错误时及时退出，避免悬挂。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go \n",
    "2025/10/01 00:41:26 已连接服务器 remote=127.0.0.1:9101 local=127.0.0.1:48842\n",
    "2025/10/01 00:41:26 收到服务器：echo: login user=alice token=123456\n",
    "2025/10/01 00:41:28 收到服务器：echo: publish room=42 body=HelloLongConn\n",
    "2025/10/01 00:41:30 收到服务器：echo: query order_id=2025001 status\n",
    "2025/10/01 00:41:42 发送心跳 PING\n",
    "2025/10/01 00:47:46 收到服务器：PONG\n",
    "^Csignal: interrupt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 111 TCP粘包问题解决"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 以下汇总 TCP 粘包的核心概念、影响与风险，为后续解决方案提供背景信息。\n",
    "\n",
    "| 维度 | 核心描述                                                     | 备注                       |\n",
    "| ---- | ------------------------------------------------------------ | -------------------------- |\n",
    "| 概念 | TCP 是**字节流**协议，单次 `Write` 与单次 `Read` 并非 1:1，对端可能一次读到多条或半条消息，即粘包/拆包 | 应用层需自定义消息边界     |\n",
    "| 影响 | 解码错位、阻塞等待、业务异常、数据错乱                       | 高频小包、长连接场景更常见 |\n",
    "| 场景 | 任意基于 TCP 的通信：IM、游戏、行情、RPC、IoT 等             | UDP 不粘包但有丢包/乱序    |\n",
    "| 风险 | 协议解析失败、状态机混乱、潜在安全隐患                       | 部署前必须压测拆包逻辑     |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 粘包产生的主要原因与触发场景一览，便于选择对应缓解手段。\n",
    "\n",
    "| 成因类别 | 具体原因                 | 典型触发方式                      | 备注                  |\n",
    "| -------- | ------------------------ | --------------------------------- | --------------------- |\n",
    "| 协议特性 | 字节流连续无边界         | `Read`/`Write` 调用次数不匹配     | 属于 TCP 基本特性     |\n",
    "| 网络层   | MSS/MTU 限制、拥塞控制   | 报文超 MTU 被拆分，未满时可能合并 | 与链路质量相关        |\n",
    "| 内核优化 | Nagle 算法、发送缓冲合并 | 短时间多次写入小包                | 可 `SetNoDelay(true)` |\n",
    "| 应用层   | 缓冲区策略、写入频率     | 未定义协议、读写逻辑不一致        | 需要封装编解包        |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 常见拆包策略对比，帮助根据业务挑选合适方案。\n",
    "\n",
    "| 解决策略   | 核心思路                         | 适用场景             | 注意事项                |\n",
    "| ---------- | -------------------------------- | -------------------- | ----------------------- |\n",
    "| 定长消息   | 每条消息固定长度                 | 固定格式、物联网等   | 变长字段需拆分或填充    |\n",
    "| 分隔符     | 特殊字符（`\\n` 等）划分消息      | 文本协议、命令式协议 | 需转义或约束内容        |\n",
    "| 长度前缀   | 前置长度字段（2/4/8 字节）       | 通用、二进制协议     | 注意大小端、字段类型    |\n",
    "| 序列化封包 | JSON/ProtoBuf/TLV 等自带 framing | 跨语言、大型系统     | 编解码开销、Schema 管理 |\n",
    "| TCP 优化   | `SetNoDelay`、调整缓冲           | 高频小包对时延敏感   | 可能增加报文数          |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 实现拆包时的实战要点与常见风险，用于自查。\n",
    "\n",
    "| 实战要点   | 推荐做法                              | 易忽视风险                    |\n",
    "| ---------- | ------------------------------------- | ----------------------------- |\n",
    "| 编解包封装 | 独立封装包头/包体；使用 `io.ReadFull` | 忘记处理剩余缓存导致死锁      |\n",
    "| 缓冲处理   | 利用 `bufio.Reader`、`bytes.Buffer`   | 缓冲未复用导致频繁 GC         |\n",
    "| 心跳与超时 | `SetReadDeadline` + Ping/Pong         | 忽略超时处理 → goroutine 堆积 |\n",
    "| 调试排查   | 打印字节长度与十六进制                | 仅看字符串难定位问题          |\n",
    "| 压测验证   | 构造高频小包、异常包                  | 未测极端情况即上线            |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 粘包现象演示（错误示例）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go \n",
    "2025/10/01 01:05:41 [NAIVE] 监听 127.0.0.1:7000，等待客户端连接...\n",
    "2025/10/01 01:05:45 [NAIVE] 单次 Read=15 字节，内容=\"helloworldstack\"\n",
    "2025/10/01 01:05:45 [NAIVE] 客户端关闭连接"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go \n",
    "2025/10/01 01:05:45 [NAIVE] 已发送 \"hello\"\n",
    "2025/10/01 01:05:45 [NAIVE] 已发送 \"world\"\n",
    "2025/10/01 01:05:45 [NAIVE] 已发送 \"stack\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 长度前缀解决方案（正确示例）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server.go \n",
    "2025/10/01 01:06:32 [LEN] 监听 127.0.0.1:7100，等待客户端连接...\n",
    "2025/10/01 01:06:39 [LEN] 收到完整消息 len=16 内容=\"login:user=alice\"\n",
    "2025/10/01 01:06:39 [LEN] 收到完整消息 len=24 内容=\"pub:topic=order body=buy\"\n",
    "2025/10/01 01:06:39 [LEN] 收到完整消息 len=4 内容=\"ping\"\n",
    "2025/10/01 01:06:39 [LEN] 客户端正常断开\n",
    "2025/10/01 01:06:39 [LEN] 关闭连接 remote=127.0.0.1:38322"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client.go \n",
    "2025/10/01 01:06:39 [LEN] 已发送 len=16 payload=\"login:user=alice\"\n",
    "2025/10/01 01:06:39 [LEN] 已发送 len=24 payload=\"pub:topic=order body=buy\"\n",
    "2025/10/01 01:06:39 [LEN] 已发送 len=4 payload=\"ping\"\n",
    "2025/10/01 01:06:39 [LEN] 收到回显：\"ack:login:user=alice\"\n",
    "2025/10/01 01:06:39 [LEN] 收到回显：\"ack:pub:topic=order body=buy\"\n",
    "2025/10/01 01:06:39 [LEN] 收到回显：\"ack:ping\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 112 socket连接的并发安全性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Go 中与 socket 并发安全相关的典型风险、触发原因以及核心防护手段，便于整体把握问题空间\n",
    "\n",
    "| 维度         | 典型表现                                                 | 风险源头                              | 解决核心                                               |\n",
    "| ------------ | -------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------ |\n",
    "| 共享状态     | 多个 goroutine 同时读写同一连接、map、缓冲区导致数据竞态 | 默认数据结构（slice/map）并非线程安全 | 使用 `sync.Mutex/RWMutex`、`sync.Map`、消息通道        |\n",
    "| 连接生命周期 | 竞争关闭、重复写入已关闭连接                             | goroutine 未协同退出、缺少上下文      | `context.Context` 控制取消、引用计数、`sync.WaitGroup` |\n",
    "| 写操作串行化 | 多 goroutine 同时 `conn.Write` 产生报文乱序/交叉         | TCP write 非原子                      | 封装写锁、独立 writer goroutine、缓冲通道              |\n",
    "| 资源治理     | 泄露 FD、goroutine、定时器                               | 缺少统一管理、异常未捕获              | 连接管理器、心跳超时、统一关闭流程                     |\n",
    "| 错误处理     | 错误被吞掉或被多个 goroutine 同时处理                    | 错误通道未统一消费                    | 集中错误通道、一次性关闭（`sync.Once`）                |\n",
    "| 背压机制     | 发送速度 > 对端消费能力导致阻塞                          | 缺少队列/限速                         | 缓冲通道、超时写、流控策略                             |\n",
    "| 监控诊断     | 并发问题难复现、定位难                                   | 缺少日志、指标                        | 结构化日志、连接 ID、状态监控                          |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 并发安全的常用策略与适用场景对照，帮助选择合适的解决方案"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 策略                        | 适用场景                           | 优点                 | 注意事项                   |\n",
    "| --------------------------- | ---------------------------------- | -------------------- | -------------------------- |\n",
    "| 读写互斥锁 (`sync.RWMutex`) | 连接表、订阅表等共享 map           | 简单直观             | 小心锁粒度、避免长时间持有 |\n",
    "| 原子操作 (`sync/atomic`)    | 计数器、状态位                     | 性能好               | 仅适用简单数值/布尔        |\n",
    "| 独立 writer goroutine       | 多 goroutine 并发写同一 `net.Conn` | 消除写乱序           | 通道需设定容量与退出信号   |\n",
    "| `context` 派发              | 处理需要取消的请求                 | 协调退出             | 防止忘记 `cancel()`        |\n",
    "| 连接管理器                  | 多客户端连接生命周期治理           | 统一注册、广播、清理 | 注意协程安全与心跳检测     |\n",
    "| `sync.Once` + 错误通道      | 保证连接只关闭一次                 | 避免重复关闭 panic   | 结合日志记录首个错误       |\n",
    "| 背压队列                    | 突发写入、广播消息                 | 防止写阻塞           | 队列满时需降级策略         |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 连接管理器 + 写锁，保障广播并发安全"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `hub` 使用 `sync.RWMutex` 管理连接 map。\n",
    "- 每个连接封装 `safeConn`，内部含写锁防止多 goroutine 同时 `Write`。\n",
    "- 使用 `context` + `WaitGroup` 协调退出。\n",
    "- 主协程集中处理广播命令，避免写入交叉。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server_hub.go \n",
    "2025/10/01 01:37:43 [server] listening on 127.0.0.1:8000\n",
    "2025/10/01 01:37:50 [hub] register 127.0.0.1:46378 (total=1)\n",
    "2025/10/01 01:36:38 [server] recv from 127.0.0.1:46378: \"message #1 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:40 [server] recv from 127.0.0.1:46378: \"message #2 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:42 [server] recv from 127.0.0.1:46378: \"message #3 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:44 [server] recv from 127.0.0.1:46378: \"message #4 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:46 [server] recv from 127.0.0.1:46378: \"message #5 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:48 [server] recv from 127.0.0.1:46378: \"message #6 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:50 [server] recv from 127.0.0.1:46378: \"message #7 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:52 [server] recv from 127.0.0.1:46378: \"message #8 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:54 [server] recv from 127.0.0.1:46378: \"message #9 from 127.0.0.1:46378\"\n",
    "2025/10/01 01:36:56 [server] recv from 127.0.0.1:46378: \"message #10 from 127.0.0.1:46378\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client_hub.go \n",
    "2025/10/01 01:37:50 [client] connected local=127.0.0.1:46378 remote=127.0.0.1:8000\n",
    "2025/10/01 01:36:38 [client] recv: [broadcast] 127.0.0.1:46378 says: message #1 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:40 [client] recv: [broadcast] 127.0.0.1:46378 says: message #2 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:42 [client] recv: [broadcast] 127.0.0.1:46378 says: message #3 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:44 [client] recv: [broadcast] 127.0.0.1:46378 says: message #4 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:46 [client] recv: [broadcast] 127.0.0.1:46378 says: message #5 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:48 [client] recv: [broadcast] 127.0.0.1:46378 says: message #6 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:50 [client] recv: [broadcast] 127.0.0.1:46378 says: message #7 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:52 [client] recv: [broadcast] 127.0.0.1:46378 says: message #8 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:54 [client] recv: [broadcast] 127.0.0.1:46378 says: message #9 from 127.0.0.1:46378\n",
    "2025/10/01 01:36:56 [client] recv: [broadcast] 127.0.0.1:46378 says: message #10 from 127.0.0.1:46378"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 单连接多生产者写入的串行化方案"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 封装 `SafeWriter`，把多个 goroutine 的写操作统一交给专用协程串行执行，避免对同一 `net.Conn` 并发写造成的数据错序或数据竞争。\n",
    "- 客户端同时启动多个 goroutine 发送消息，借由 `SafeWriter` 保证写入安全。\n",
    "- 服务器负责简单回显，方便观察写入结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run server_writer.go \n",
    "2025/10/01 01:42:59 [writer-server] 监听地址 127.0.0.1:8100\n",
    "2025/10/01 01:43:05 [writer-server] 新连接 remote=127.0.0.1:50834\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-2 的消息-1\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-3 的消息-1\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-4 的消息-1\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-1 的消息-1\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-0 的消息-1\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-4 的消息-2\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-3 的消息-2\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-2 的消息-2\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-1 的消息-2\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-0 的消息-2\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-1 的消息-3\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-0 的消息-3\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-3 的消息-3\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-2 的消息-3\"\n",
    "2025/10/01 01:43:05 [writer-server] 收到: \"写协程-4 的消息-3\"\n",
    "2025/10/01 01:43:05 [writer-server] 连接关闭 remote=127.0.0.1:50834"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run client_writer.go \n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-2 的消息-1 @ 2025-10-01T01:43:05.644946818+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-3 的消息-1 @ 2025-10-01T01:43:05.645053787+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-4 的消息-1 @ 2025-10-01T01:43:05.645103509+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-1 的消息-1 @ 2025-10-01T01:43:05.645150346+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-0 的消息-1 @ 2025-10-01T01:43:05.645197063+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-4 的消息-2 @ 2025-10-01T01:43:05.74520809+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-3 的消息-2 @ 2025-10-01T01:43:05.745359271+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-2 的消息-2 @ 2025-10-01T01:43:05.745381152+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-1 的消息-2 @ 2025-10-01T01:43:05.745391732+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-0 的消息-2 @ 2025-10-01T01:43:05.745404285+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-1 的消息-3 @ 2025-10-01T01:43:05.846149668+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-0 的消息-3 @ 2025-10-01T01:43:05.846242831+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-3 的消息-3 @ 2025-10-01T01:43:05.846272927+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-2 的消息-3 @ 2025-10-01T01:43:05.846289438+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 收到回显: 服务器回显: 写协程-4 的消息-3 @ 2025-10-01T01:43:05.846381619+08:00\n",
    "2025/10/01 01:43:05 [writer-client] 写入全部完成，准备关闭 SafeWriter\n",
    "2025/10/01 01:43:05 [writer-client] 读取协程退出：连接已关闭（预期）\n",
    "2025/10/01 01:43:05 [writer-client] 客户端退出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 113 练习：基于socket编程实现支持高并发的RPC server"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 主题                                           | 核心概念                                                 | 关键要点                                                    | 实战提示                                                     |\n",
    "| ---------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------ |\n",
    "| RPC 调用链 (Socket → 序列化 → 网络 → 反序列化) | 远程过程调用通过统一协议封装请求/响应                    | 客户端封装请求 → 网络传输 → 服务器解析并执行业务 → 回写响应 | 明确协议字段、类型及误包处理；保持客户端与服务器协议一致     |\n",
    "| JSON 序列化                                    | 将结构体转 JSON 字节进行跨语言传输                       | `json.Marshal` → `[]byte`；`json.Unmarshal` → 结构体        | 控制字段命名（`json` tag）；避免大对象频繁分配，可使用对象池复用 |\n",
    "| UDP 报文传输                                   | 面向无连接、报文边界天然存在                             | 单个报文大小 < MTU；无需粘包处理，但要考虑丢包              | 加入请求 ID、重试/超时机制；与 TCP 相比需手动做可靠性补偿    |\n",
    "| 并发 Goroutine                                 | 服务器每个请求独立处理，客户端可批量并发压测             | `sync.WaitGroup` 协调收尾，`select` + `timer` 做超时        | 控制 goroutine 数量，使用池化避免资源耗尽                    |\n",
    "| 连接管理与 Select                              | UDP 使用 `ReadFromUDP` 获取远端地址，响应需 `WriteToUDP` | `net.ListenUDP` & `net.DialUDP`；`select` 结合超时防止阻塞  | 客户端需要持久化远端地址，服务器记录请求元信息便于回包       |\n",
    "| 压力测试策略                                   | 构造 100/500/1000 等批量请求检验并发能力                 | 通过 `sync.WaitGroup` 等待全部完成                          | 输出耗时与成功率；模拟异常（丢包、超时）评估健壮性           |\n",
    "| 错误处理与日志                                 | 区分业务错误、网络错误、序列化错误                       | 结构化日志记录 RequestID/RemoteAddr                         | 用统一错误通道或日志工具，便于排查                           |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行效果预期\n",
    "\n",
    "- 服务器端日志会显示每个请求的 `request_id`、远端地址及求和结果。\n",
    "- 客户端日志会打印成功请求的明细，若出现超时或错误，也会标注请求 ID，方便排查。\n",
    "- 可调整 `concurrentClient`、`requestPerClient`、`clientTimeout` 等参数进行压测或异常模拟。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run udp_rpc_server.go \n",
    "2025/10/01 01:49:09 [server] UDP 服务启动，监听 127.0.0.1:9000\n",
    "2025/10/01 01:49:22 [server] 收到请求 id=client1-0-1759254562649982304 remote=127.0.0.1:44653 payload=a:81.86756056179811 b:19.344688763378205\n",
    "2025/10/01 01:49:22 [server] 收到请求 id=client2-0-1759254562650030654 remote=127.0.0.1:44653 payload=a:74.71234948690498 b:0.4074367214664675\n",
    "2025/10/01 01:49:22 [server] 收到请求 id=client0-0-1759254562650179300 remote=127.0.0.1:44653 payload=a:68.58277597070962 b:22.017889330393345\n",
    "2025/10/01 01:49:22 [server] 收到请求 id=client4-0-1759254562649954011 remote=127.0.0.1:44653 payload=a:17.788520881444533 b:52.138413487817516\n",
    "2025/10/01 01:49:22 [server] 收到请求 id=client3-0-1759254562650060890 remote=127.0.0.1:44653 payload=a:96.26717039036615 b:13.522888328314519\n",
    "2025/10/01 01:49:22 [server] 请求完成 id=client1-0-1759254562649982304 sum=101.21224932517632 耗时=384.555µs\n",
    "2025/10/01 01:49:22 [server] 请求完成 id=client2-0-1759254562650030654 sum=75.11978620837145 耗时=450.117µs\n",
    "2025/10/01 01:49:22 [server] 请求完成 id=client0-0-1759254562650179300 sum=90.60066530110296 耗时=427.665µs\n",
    "2025/10/01 01:49:22 [server] 请求完成 id=client4-0-1759254562649954011 sum=69.92693436926206 耗时=367.102µs\n",
    "2025/10/01 01:49:22 [server] 请求完成 id=client3-0-1759254562650060890 sum=109.79005871868067 耗时=318.311µs\n",
    "\n",
    "... ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! go run udp_rpc_client.go \n",
    "2025/10/01 01:49:22 [client] 已连接到 127.0.0.1:9000\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client2-0-1759254562650030654 a=74.71234948690498 b=0.4074367214664675 sum=75.11978620837145\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client3-0-1759254562650060890 a=96.26717039036615 b=13.522888328314519 sum=109.79005871868067\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client1-0-1759254562649982304 a=81.86756056179811 b=19.344688763378205 sum=101.21224932517632\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client4-0-1759254562649954011 a=17.788520881444533 b=52.138413487817516 sum=69.92693436926206\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client0-0-1759254562650179300 a=68.58277597070962 b=22.017889330393345 sum=90.60066530110296\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client1-1-1759254562671297099 a=50.93169087509006 b=91.49719944608945 sum=142.42889032117952\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client4-1-1759254562671211460 a=15.613091712850766 b=88.50059916429447 sum=104.11369087714523\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client0-1-1759254562671293181 a=5.977915447473456 b=19.075379270535766 sum=25.053294718009223\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client3-1-1759254562671356059 a=89.68628095559282 b=75.55950344836462 sum=165.24578440395743\n",
    "2025/10/01 01:49:22 [client] 请求成功 id=client2-1-1759254562671370716 a=48.168050874307575 b=42.32551211777259 sum=90.49356299208017\n",
    "\n",
    "... ...\n",
    "\n",
    "2025/10/01 01:49:23 [client] 全部请求发送完成，总耗时 423.231025ms\n",
    "2025/10/01 01:49:23 [client] 读取响应失败: read udp 127.0.0.1:44653->127.0.0.1:9000: use of closed network connection"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Go (gonb)",
   "language": "go",
   "name": "gonb"
  },
  "language_info": {
   "codemirror_mode": "",
   "file_extension": ".go",
   "mimetype": "text/x-go",
   "name": "go",
   "nbconvert_exporter": "",
   "pygments_lexer": "",
   "version": "go1.24.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
